package cn.xinfei.xdecision.data.core.frame.retry;

import cn.xinfei.xdecision.common.model.datax.enums.RetryStrategy;
import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataException;
import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataExceptionType;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Data
public class OkHttpRetryInterceptor implements Interceptor {

    private int maxRetryNum;
    private AtomicInteger retryNum = new AtomicInteger(0);
    private long retryTimeout = 10000;
    private RetryStrategy retryStrategy = RetryStrategy.SIMPLE_RETRY_POLICY;
    private static final Integer RETRY_INTERVAL = 100;
    private static final Integer MAX_TIME_OUT_RETRY_NUM = 3;


    public OkHttpRetryInterceptor(int maxRetryNum) {
        this.maxRetryNum = maxRetryNum;
    }

    public OkHttpRetryInterceptor(int maxRetryNum,
                                  RetryStrategy retryStrategy,
                                  long retryTimeout) {
        this.maxRetryNum = maxRetryNum;
        this.retryStrategy = retryStrategy;
        this.retryTimeout = retryTimeout;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        log.info("reqUrl = {}", request.url().toString());
        long startTime = System.currentTimeMillis();
        Response response = chain.proceed(request);
        long endTime = System.currentTimeMillis();
        if (!response.isSuccessful()) {
            log.info("执行 retryStrategy = [{}] 重试策略 retryNum = {}", retryStrategy.getName(), retryNum.get());
            switch (retryStrategy) {
                case NEVERY_RETRY_POLICY:
                    log.info("当前重试策略【NEVERY_RETRY_POLICY】 请求不允许重试!");
                    return response;
                case SIMPLE_RETRY_POLICY:
                    log.info("当前重试策略【SIMPLE_RETRY_POLICY】 允许重试次数 = {} 开始重试！当前已重试 ={}次", maxRetryNum, retryNum.get());
                    while (!response.isSuccessful() && retryNum.get() < maxRetryNum) {
                        retryNum.incrementAndGet();
                        response.close();
                        try {
                            response = chain.proceed(request);
                            log.info("当前重试策略【SIMPLE_RETRY_POLICY】 已重试次数 = {}", retryNum.get());
                        } catch (Exception e) {
                            log.error("当前重试策略【SIMPLE_RETRY_POLICY】 当前已重试 ={}次 重试失败!", retryNum.get(), e);
                        }
                    }
                    if (!response.isSuccessful()) {
                        log.error("当前重试策略【SIMPLE_RETRY_POLICY】重试结束 ，http 重试失败，重试次数 = {}，failCode = {}, for = {}", retryNum.get(), response.code(), response.message());
                    }
                    return response;
                case TIMOUT_RETRY_POLICY:
                    while (!response.isSuccessful() && retryTimeout / 25 > (endTime - startTime) && retryNum.get() < MAX_TIME_OUT_RETRY_NUM) {
                        log.info("当前重试策略【TIMOUT_RETRY_POLICY】 开始超时重试 超时时间 = {} milliSeconds 当前已重试 ={}次", retryTimeout, retryNum.get());
                        retryNum.incrementAndGet();
                        response.close();
                        try {
                            response = chain.proceed(request);
                            log.info("当前重试策略【TIMOUT_RETRY_POLICY】 已重试次数 = {}", retryNum.get());
                        } catch (Exception e) {
                            log.error("当前重试策略【TIMOUT_RETRY_POLICY】 当前已重试 = {}次 重试失败!", retryNum.get(), e);
                        }
                        endTime = System.currentTimeMillis();
                    }
                    if (!response.isSuccessful()) {
                        log.error("当前重试策略【TIMOUT_RETRY_POLICY】重试结束 ，http 重试失败，，重试次数 = {} failCode = {}, for = {}", retryNum.get(), response.code(), response.message());
                    }
                    return response;

                case DELAY_RETRY_POLICY:
                    while (!response.isSuccessful() && retryNum.get() < maxRetryNum) {
                        try {
                            int delayTimes = RETRY_INTERVAL ^ retryNum.get();
                            log.info("当前重试策略【DELAY_RETRY_POLICY】 Wait for {} millsecons", delayTimes);
                            Thread.sleep(delayTimes);
                        } catch (final InterruptedException ignored) {
                            log.info("当前重试策略【DELAY_RETRY_POLICY】 thread = {} is interruptec", Thread.currentThread().getName());
                            Thread.currentThread().interrupt();
                        }
                        retryNum.incrementAndGet();
                        response.close();
                        try {
                            response = chain.proceed(request);
                            log.info("当前重试策略【DELAY_RETRY_POLICY】 已重试次数 = {}", retryNum.get());
                        } catch (Exception e) {
                            log.error("当前重试策略【DELAY_RETRY_POLICY】 当前已重试 ={} 次 重试失败!", retryNum.get(), e);
                        }
                    }
                    if (!response.isSuccessful()) {
                        log.error("当前重试策略【DELAY_RETRY_POLICY】重试结束 ，http 重试失败，重试次数 = {} failCode = {}, for = {}", retryNum.get(), response.code(), response.message());
                    }
                    return response;
                default:
                    throw new XDecisionDataException(XDecisionDataExceptionType.DATA_HTTP_CLIENT_RETRY_POLICY_ERROR);
            }
        }
        return response;

    }

}
